#include "StdAfx.h"
//-------------------------------------------------------------------------------------------------
//ImageProcStepLibs.cpp  - Collection of ImageProcStep*Lib libraries - used by ArchiveProc        
//-------------------------------------------------------------------------------------------------
//FreeBSD License
//Copyright 2012 Will Stott All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are
//permitted provided that the following conditions are met:
//
//   1. Redistributions of source code must retain the above copyright notice, this list of
//      conditions and the following disclaimer.
//
//   2. Redistributions in binary form must reproduce the above copyright notice, this list
//      of conditions and the following disclaimer in the documentation and/or other materials
//      provided with the distribution.
//
//THIS SOFTWARE IS PROVIDED BY Will Stott ``AS IS'' AND ANY EXPRESS OR IMPLIED
//WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Will Stott OR
//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//The views and conclusions contained in the software and documentation are those of the
//authors and should not be interpreted as representing official policies, either expressed
//or implied, of Will Stott.
//
//----Authors--------------------------------------------------------------------------------------
//created by: will.stott@ucl.ac.uk Feb 2012
//
//----Comments-------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------
#include <Strsafe.h>

#include "TinyXmlLib.h"
#include "TinyXmlHelper.h"
#include "ImageExptLib.h"

#include "ImageProcStepLib.h"
#include "ImageProcStepLibs.h"
#include "ImageProcStepLibAuto.h"
#include "IImageProcFn.h"
#include "ImageExpt.h"

const char * const ImageProcStepLibs::BadDllName = "[BadDllName]";
const char * const ImageProcStepLibs::BadDllPathFile = "[BadDllPathFile]";
const char * const ImageProcStepLibs::BadDllVersion = "[BadDLLVersion]";
const char * const ImageProcStepLibs::BadDllPath = "[BadDLLPath]";

ImageProcStepLibs::ImageProcStepLibs(void) :
	_pStrFilePathNameBuffer('\0'),
	_ImageProcLibDir(ImageProcStepLibs::BadDllPath)
{
	_pStrFilePathNameBuffer = (char *) ::malloc( (ImageExptLib::MaxPathFileNameLen+1) * sizeof(char));
	_pwStrFilePathNameBuffer = (wchar_t *) ::malloc((ImageExptLib::MaxPathFileNameLen+1) * sizeof(wchar_t));
}


ImageProcStepLibs::~ImageProcStepLibs(void)
{
	ClearAllLibs();		

	if (_pStrFilePathNameBuffer != '\0')
	{
		::free(_pStrFilePathNameBuffer);
		_pStrFilePathNameBuffer = '\0';
	}
	if (_pwStrFilePathNameBuffer != '\0')
	{
		::free(_pwStrFilePathNameBuffer);
		_pStrFilePathNameBuffer = '\0';
	}
}

bool	ImageProcStepLibs::LoadLibs(const wchar_t *ImageProcDLLdirectory)
{
	bool rc = false;

	if (_pwStrFilePathNameBuffer == '\0')
	   ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "ERROR: _pwStrFilePathNameBuffer is null");
	else
	{
		WIN32_FIND_DATAW  ffd;
		HANDLE hFind = INVALID_HANDLE_VALUE;

		if (ImageExptLib::ConvertwStrToStr(_pStrFilePathNameBuffer, ImageExptLib::MaxPathFileNameLen, ImageProcDLLdirectory, (size_t)ImageExptLib::MaxPathNameLen) == '\0')
			ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "ERROR: failed to convert input directory path parameter");
		else
		{
			std::string path(_pStrFilePathNameBuffer);
			int endPathPos = path.find_last_of('\\');
			if (endPathPos != path.length()-1)
				ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "ERROR: path parameter must end with backslash: %s", path.c_str());
			else
			{
				try
				{

					if (StringCchCopyW(_pwStrFilePathNameBuffer, ImageExptLib::MaxPathNameLen, ImageProcDLLdirectory) != S_OK )
						ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "ERROR: path parameter could not be copied: %s", path.c_str());
					else
					{		//todo: ImageStepProcLib libraries are contained in their own subdirectory off ImageProcDLLdirectory - this supports different versions and their dependencies on particular versions of 3rd party DLLs
						if (StringCchCatW(_pwStrFilePathNameBuffer, ImageExptLib::MaxPathFileNameLen, L"*.dll") != S_OK)
							ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "ERROR: .dll could not be added to path parameter: %s", path.c_str());
						else
						{
							hFind = FindFirstFileW(_pwStrFilePathNameBuffer, &ffd);
							if (INVALID_HANDLE_VALUE == hFind)
							{
								DWORD errNo = ::GetLastError();
								if (errNo != ERROR_FILE_NOT_FOUND )
									ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "ERROR: failed to find first file, error=%d, directory: %s*.dll", errNo, path.c_str() );
								else
									rc = true;
							}
							else
							{
								DWORD dwError=0;
								do
								{
									if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == false)
									{
										if (ImageExptLib::ConvertwStrToStr (_pStrFilePathNameBuffer, ImageExptLib::MaxPathFileNameLen, ffd.cFileName, (size_t)ImageExptLib::MaxFileNameLen) == '\0')
											ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "ERROR: failed to convert dll filename");
										else
										{
											std::string pathfilename(path);
											pathfilename += _pStrFilePathNameBuffer;
											ImageProcStepLib *DynLib = '\0';
											if ( (DynLib = ImageProcStepLibs::CreateImageProcStepLib(pathfilename.c_str())) != '\0')
											{
												AddLib(DynLib);
												ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "SUCCESS: ImageProcStepLib DLL added: %s", pathfilename.c_str() );
											}
										}
									}
								}
								while (FindNextFileW(hFind, &ffd) != 0);
								dwError = GetLastError();
								if (dwError != ERROR_NO_MORE_FILES) 
									ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "ERROR: failed to find next file, error=%d, directory: %s", dwError, path.c_str() );
								FindClose(hFind);
								hFind = INVALID_HANDLE_VALUE;
								_ImageProcLibDir = path;
								rc = true;
							}
						}
					}
				}
				catch(...)
				{
					ImageExptLib::LogMsg("ImageProcStepLibs::LoadLibs", "ERROR: Unexpected exception");
				}
				if ((hFind != INVALID_HANDLE_VALUE) && (rc != true))
					FindClose(hFind);
			}
		}
	}
	return rc;
}

bool	ImageProcStepLibs::AddLib(ImageProcStepLib *lib)
{
	bool rc = false;

	if ((lib == '\0') || (lib->GetDynLib() == '\0'))
		ImageExptLib::LogMsg("ImageProcStepLibs::AddLib", "ERROR: lib param is null or not initialised as GetDynLib() == null");	
	else
	{
		if (lib->GetDynLib()->IsApiValid() == false)
			ImageExptLib::LogMsg("ImageProcStepLibs::AddLib", "ERROR: %s IsApiValid() failed", lib->GetDynLib()->GetLibName());	
		else
		{
			if ( GetLibPosition(lib->GetDynLib()->GetLibName(), lib->GetDynLib()->GetLibVersion()) != ImageProcStepLibAuto::NotFound)
				ImageExptLib::LogMsg("ImageProcStepLibs::AddLib", "ERROR: %s already in libs", lib->GetDynLib()->GetLibName());	
			else
			{
				_Libs.push_back(lib);
				rc = true;
			}
		}
	}
	return rc;
}

IImageProcStepLibDyn *ImageProcStepLibs::GetLib(int position)
{ 
	IImageProcStepLibDyn *rc = '\0';

	int cnt = static_cast<int>(_Libs.size());

	if ((position > (cnt-1)) || (position < 0))
		ImageExptLib::LogMsg("ImageProcStepLibs::GetLib(position)", "ERROR: position %d is invalid (_Libs.size()=%d)", position, cnt);
	else
		rc = _Libs[position]->GetDynLib(); 

	return rc;
}

IImageProcStepLibDyn *ImageProcStepLibs::GetLib(const char *LibName, const char *LibVer, int iVer)
{
	IImageProcStepLibDyn *rc = '\0';

	if ((LibName == '\0') || (LibVer == '\0'))
		ImageExptLib::LogMsg("ImageProcStepLibs::GetLib(name)", "ERROR: input param LibName is %s, LibVer is %s", (LibName == '\0') ? "null" : "no null", (LibVer == '\0') ? "null" : "no null");
	else
	{
		int pos = ImageProcStepLibs::NotFound;
		if ( (pos = GetLibPosition(LibName, LibVer)) == ImageProcStepLibs::NotFound)
			ImageExptLib::LogMsg("ImageProcStepLibs::GetLib(name)", "ERROR: %s %s not found", LibName, LibVer);
		else
		{
			int libiVer = _Libs[pos]->GetDynLib()->GetLibInterfaceVersion();
			if ( libiVer < iVer)	//later versions of ArchiveProc ImageProcStepLib interface must support earlier versions as implemented in ImageProcStepLib libaries
				ImageExptLib::LogMsg("ImageProcStepLibs::GetLib(name)", "ERROR: %s %s found, but it supports an earlier interface (v%d) rather than the required v%d", LibName, LibVer, libiVer, iVer);
			else
				rc = _Libs[pos]->GetDynLib();
		}
	}
	return rc;
}

bool ImageProcStepLibs::ClearAllLibs()
{
	bool rc = false;

	size_t cnt = _Libs.size();
	if (cnt > 0 )
	{
		for (size_t x = 0; x < cnt; x++)
			delete  _Libs[x];
		_Libs.clear();
		rc = true;
	}
	return rc;
}

//-------------------------------------------------------------------------------------------------
//			STATIC
//-------------------------------------------------------------------------------------------------


ImageProcStepLib *ImageProcStepLibs::CreateImageProcStepLib(const char *pathfilenameDll)
{
	ImageProcStepLib *rc = '\0';

	if (pathfilenameDll != '\0')
	{
		wchar_t	wcTempBuffer[ImageExptLib::MaxPathFileNameLen+1];
		const wchar_t *pwStrPathFileName = '\0';

		if ((pwStrPathFileName = ImageExptLib::ConvertStrTowStr(wcTempBuffer, ImageExptLib::MaxPathFileNameLen, pathfilenameDll, (size_t)ImageExptLib::MaxPathFileNameLen)) == '\0')
			ImageExptLib::LogMsg("ImageProcStepLibs::CreateImageProcStepLibs", "ERROR: ImageExptLib::ConvertStrTowStr() failed for %s", pathfilenameDll);
		else
		{
			HINSTANCE	hInstance = NULL;	
			try
			{
				if((hInstance = ::LoadLibraryW(wcTempBuffer)) == NULL)
					ImageExptLib::LogMsg("ImageProcStepLibs::CreateImageProcStepLibs", "ERROR: ::LoadLibrary returned errorcode=%d for %s", GetLastError(), pathfilenameDll);
				else
				{
					IImageProcStepLibDyn *dynLib = '\0';
					IImageProcStepLibDyn * (*pfn)(void) = 0;
					*(FARPROC *)&pfn = ::GetProcAddress(hInstance, ImageExptLib::CreateFunctionName);
					if (pfn == NULL)
						ImageExptLib::LogMsg("ImageProcStepLibs::CreateImageProcStepLibs", "ERROR: ::GetProcAddress returned NULL, %s not found in %s",  ImageExptLib::CreateFunctionName, pathfilenameDll);
					else
					{
						if ((dynLib = pfn()) == '\0')		//this is where the ctor in the DLL is invoked - see ImageProcSamleLib: ImageProcSampleLib::ImageProcSampleLib(void)
							ImageExptLib::LogMsg("ImageProcStepLibs::CreateImageProcStepLibs", "ERROR: IImageProcStepLibDyn ctor failed for pfn() returned by %s in %s",  ImageExptLib::CreateFunctionName, pathfilenameDll);
						else
						{
							int iver = dynLib->GetLibInterfaceVersion();
							if ( (dynLib->IsApiValid() == false) || (iver != ImageProcStepLibAuto::InterfaceVersion))
							{
								ImageExptLib::LogMsg("ImageProcStepLibs::CreateImageProcStepLibs", "ERROR: IsApiValid()=%s,  LibInterface=%d (this app expects %d) for IImageProcStepLibDyn returned by %s in %s",  (dynLib->IsApiValid() == false) ? "fail" : "pass", iver, ImageProcStepLibAuto::InterfaceVersion, ImageExptLib::CreateFunctionName, pathfilenameDll);
								delete dynLib;
								dynLib = '\0';
							}
							else
							{
								ImageProcStepLib *lib = '\0';
								if ((lib = new ImageProcStepLib(hInstance, dynLib)) == '\0') 
									ImageExptLib::LogMsg("ImageProcStepLibs::CreateImageProcStepLibs", "ERROR:  new ImageProcStepLib failed for IImageProcStepLibDyn returned by %s in %s",  ImageExptLib::CreateFunctionName, pathfilenameDll);
								else
								{
									if(lib->IsValid() == false)
										ImageExptLib::LogMsg("ImageProcStepLibs::CreateImageProcStepLibs", "ERROR: ImageProcStepLib->IsValid() failed hInstance=%s for IImageProcStepLibDyn returned by %s in %s",  (hInstance == NULL) ? "NULL" : "not NULL", ImageExptLib::CreateFunctionName, pathfilenameDll);
									else
										rc = lib;		//success!!!
								}
							}
						}
					}
				}
			}
			catch(...)
			{
				ImageExptLib::LogMsg("ImageProcStepLibs::CreateImageProcStepLib", "ERROR: Unexpected exception");
			}
			if ((hInstance != NULL) && (rc == '\0'))
				::FreeLibrary(hInstance);		//if rc != '\0', then this handle is released by the DLL (see ImageProcSampleLib::~ImageProcSampleLib()) when ImageProcStepLibs::ClearAllLibs() deletes this pointer
		}
	}
	return rc;
}

//-------------------------------------------------------------------------------------------------
//			PRIVATE
//-------------------------------------------------------------------------------------------------

int	ImageProcStepLibs::GetLibPosition(const char *LibName, const char *LibVer)
{
	int rc = ImageProcStepLibs::NotFound;

	if (LibName != '\0')
	{
		std::string libname(LibName);
		std::string libver(LibVer);
		size_t cnt = _Libs.size();
		if (cnt > 0 )
		{
			for (size_t x = 0; x < cnt; x++)
			{
				if ( _Libs[x]->GetDynLib() == '\0')	//belt and braces - AddLib() doesn't allow you to add invalid objects
					ImageExptLib::LogMsg("ImageProcStepLibs::GetLibPosition", "ERROR: List unexpectedly contains a ImageProcStepLib with invalid DynLib");
				else
				{
					if ((libname == _Libs[x]->GetDynLib()->GetLibName()) && (libver == _Libs[x]->GetDynLib()->GetLibVersion()))
					{
						rc = x;
						break;
					}
				}
			}
		}
	}
	return rc;
}


//-------------------------------------------------------------------------------------------------
//			E N D   O F   F I L E
//-------------------------------------------------------------------------------------------------



